package adm

import (
	"reflect"
	"time"
	"git.oschina.net/janpoem/go-agi"
	"strings"
)

const (
	TagColumn = "col"
	TagPk = "pk"
	TagOn = "on"

	TagValNothing = ""
	TagValPkAi = "ai"
	TagValPkNormal = "nor"

	OnCreate = "create"
	OnUpdate = "update"
)

type Model interface {
	GetRow() *ResultRow
	Conn() *Connection
	Table() string
	IsExists() bool
	IsNew() bool
	// Event Api
	OnBind() // 增加一个OnBind接口
	BeforeUpdate() EventSignal
	BeforeCreate() EventSignal
	BeforeSave() EventSignal
	BeforeDestroy() EventSignal
	AfterUpdate()
	AfterCreate()
	AfterSave()
	AfterDestroy()
}

//func mkQuery(obj Model, query *QueryBuilder) *QueryBuilder {
//    if query == nil {
//        query = Select("*")
//    }
//    if len(query.Columns) <= 0 {
//        query.Cols("*")
//    }
//    return query.Table(obj.Table())
//}
//
//func Find(obj Model, query *QueryBuilder) *ResultSet {
//    return obj.Conn().Find(mkQuery(obj, query))
//}
//
//func FindOne(obj Model, query *QueryBuilder) Model {
//    rs := obj.Conn().Find(mkQuery(obj, query).Limit(1))
//    if rs.Count() > 0 {
//        rs.Row(0).Fetch(obj)
//    }
//    return obj
//}

func Save(obj Model) State {
	if obj.IsNew() {
		return Create(obj)
	} else {
		return Upgrade(obj)
	}
	return StateNothing
}

func Bind(row *ResultRow, obj Model) Model {
	if row == nil {
		return nil
	}
	valueOf := agi.ValueOf(obj)
	mapping := GetModelMapping(obj)
	// 将记录行绑定到Model
	valueOf.FieldByName("ResultRow").Set(reflect.ValueOf(row))

	for i := 0; i < mapping.Size; i++ {
		col := mapping.Columns[i]
		field := valueOf.Field(col.Index)
		raw := row.Str(col.Column)
		assignFieldValue(field, raw)
	}

	obj.OnBind()
	return obj
}

// 创建，需要补充两个字段，自增主键，和创建时间
func Create(obj Model) State {
	if obj.IsExists() {
		return StateFail
	}
	if obj.BeforeCreate() == EventBreak || obj.BeforeSave() == EventBreak {
		return StateFail
	}
	now := time.Now()
	valueOf := agi.ValueOf(obj)
	mapping := GetModelMapping(obj)
	size := mapping.Size
	// 标记是否需要跳过主键字段
	exceptPk := !PkValid(obj)
	if exceptPk {
		size -= 1
	}
	// struct的属性结构是静态的，所以可以以他的Size来预定填充
	// 这样可以减少每次动态调整尺寸的次数
	pushIndex := 0
	columns := make([]string, size)
	values := make([]interface{}, size)
	// 需要修改obj的值
	// 这里要修改的值，一定不会超过字段的总长度
	changedIndex := 0
	changedFields := make([]reflect.Value, mapping.Size)
	changedValues := make([]interface{}, mapping.Size)

	for i := 0; i < mapping.Size; i++ {
		col := mapping.Columns[i]
		// 跳过主键
		if exceptPk && col == mapping.Pk {
			continue
		}
		field := valueOf.Field(col.Index)
		columns[pushIndex] = col.Column
		var val interface{}
		// 避免碰到一些异常的情况，检查一下字段是否有效
		// 如果碰到无效的话，字段还是照样添加，但是给一个nil值，因为Columns是提前映射出来的
		if !field.IsValid() {
			val = nil
		} else if col.Column == "updated_at" || col.Column == "created_at" {
			changedFields[changedIndex] = field // 直接把要变更的字段传过去
			changedValues[changedIndex] = now
			changedIndex++
			val = now.Unix()
		} else {
			val = filterSaveValue(field)
		}
		// 过滤要保存的字段的值
		values[pushIndex] = val
		pushIndex++
	}

	rs := obj.Conn().Insert(Insert(obj.Table()).Cols(columns...).Values(values...))
	// 写入执行结果的状态
	valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))
	if rs.IsCreate() {
		// 写入主键
		if mapping.PkMode == PkAi {
			assignFieldValue(valueOf.Field(mapping.Pk.Index), rs.LastInsertId)
		}
		// 写入其他的更改字段
		for j := 0; j < changedIndex; j++ {
			// 改为调用统一的方法
			assignFieldValue(changedFields[j], changedValues[j])
		}
		obj.AfterCreate()
		obj.AfterSave()
		return StateUpdated
	}
	return StateFail
}

// 更新操作，数据实例 本身已经持有数据，更新操作需要将本身 实例 所持有的数据和数据源进行差异化比较
// 目前比较使用字符串的比较
func Upgrade(obj Model) State {
	if obj.IsNew() {
		return StateFail
	}
	if obj.BeforeUpdate() == EventBreak || obj.BeforeSave() == EventBreak {
		return StateFail
	}
	row := obj.GetRow()
	now := time.Now()
	valueOf := agi.ValueOf(obj)
	mapping := GetModelMapping(obj)
	update := Update(obj.Table())
	// 这个是用来标记更新的字段数量
	updateCount := 0
	// 这个是用来记录，需要变更的字段
	changedIndex := 0
	changedFields := make([]reflect.Value, mapping.Size)
	changedValues := make([]interface{}, mapping.Size)
	pkValid := PkValid(obj)
	// 当updateCount > 0的时候，才追加填充更新的字段，初始比较的时候并不默认填充这些字段的值
	autoFillIndex := 0
	autoFillValues := make([]interface{}, mapping.Size)
	autoFillColumns := make([]string, mapping.Size) // 数据库的字段

	for i := 0; i < mapping.Size; i++ {
		col := mapping.Columns[i]
		field := valueOf.Field(col.Index)
		var hitUpdate = false
		var val interface{}
		if col.Column == "updated_at" {
			changedValues[changedIndex] = now
			changedFields[changedIndex] = field
			changedIndex++
			autoFillValues[autoFillIndex] = now.Unix()
			autoFillColumns[autoFillIndex] = col.Column
			autoFillIndex++
		} else if (isDiff(field, row.Get(col.Column))) {
			val = filterSaveValue(field)
			hitUpdate = true
		}

		if hitUpdate {
			update.Set(col.Column, val)
			updateCount++
		}
		// 没主键，就用这条数据的每个字段作为查询条件——很血腥的
		if !pkValid || mapping.PkMode == PkNone {
			update.In(col.Column, row.Str(col.Column))
		} else if pkValid && mapping.Pk == col {
			update.In(col.Column, row.Str(col.Column))
		}
	}
	if updateCount > 0 {
		// 补充自动填充的字段
		for k := 0; k < autoFillIndex; k++ {
			update.Set(autoFillColumns[k], autoFillValues[k])
		}
		rs := obj.Conn().Update(update)
		valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))
		if rs.IsUpdate() {
			// 写入其他的更改字段
			for j := 0; j < changedIndex; j++ {
				assignFieldValue(changedFields[j], changedValues[j])
			}
			obj.AfterUpdate()
			obj.AfterSave()
			return StateUpdated
		}
		return StateFail
	}
	return StateNothing
}

func Destroy(obj Model) State {
	if obj.IsNew() {
		return StateFail
	}
	if obj.BeforeDestroy() == EventBreak {
		return StateFail
	}
	row := obj.GetRow()
	valueOf := agi.ValueOf(obj)
	mapping := GetModelMapping(obj)
	pkValid := PkValid(obj)
	delete := Delete(obj.Table())

	for i := 0; i < mapping.Size; i++ {
		col := mapping.Columns[i]
		// 没主键，就用这条数据的每个字段作为查询条件——很血腥的
		if !pkValid || mapping.PkMode == PkNone {
			delete.In(col.Column, row.Str(col.Column))
		} else if pkValid && mapping.Pk == col {
			delete.In(col.Column, row.Str(col.Column))
		}
	}

	rs := obj.Conn().Delete(delete)
	valueOf.FieldByName("ExecResult").Set(reflect.ValueOf(rs))

	if rs.IsDelete() {
		obj.AfterDestroy()
		return StateUpdated
	}
	return StateFail
}

func filterSaveValue(field reflect.Value) interface{} {
	v := field.Interface()
	switch v.(type) {
		case time.Time :
		if t, ok := v.(time.Time); ok {
			if agi.IsValidTime(t) {
				return t.Unix()
			} else {
				return 0
			}
		}
	}
	return v
}

func isDiff(value reflect.Value, raw []byte) bool {
	str := string(raw)
	typ := value.Type()
	kind := typ.Kind()
	switch kind {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64 :
		return agi.StrToFInt64(str) != value.Int()
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 :
		return agi.AnyToUInt64(str) != value.Uint()
		case reflect.Float32, reflect.Float64 :
		return agi.StrToFloat(str) != value.Float()
		case reflect.String :
		return !strings.EqualFold(str, value.String())
		default :
		if typ.String() == "time.Time" {
			if compare, ok := (value.Interface()).(time.Time); ok {
				intTime := agi.AnyToInt64(str)
				compareValue := compare.Unix()
				if !agi.IsValidTime(compare) {
					compareValue = 0
				}
				return compareValue != intTime
			}
		}
	}
	return false
}

// 从结果行（ResultRow）绑定对象属性，他应该和isDiff成一对
func setFieldValue(value reflect.Value, raw []byte) {
	str := string(raw)
	typ := value.Type()
	kind := typ.Kind()
	switch kind {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 :
		value.SetInt(agi.StrToFInt64(str))
		case reflect.Float32, reflect.Float64 :
		value.SetFloat(agi.StrToFloat(str))
		case reflect.String :
		value.SetString(str)
		default :
		if typ.String() == "time.Time" {
			rawTime := agi.IntToTime(agi.StrToFInt64(str))
			value.Set(reflect.ValueOf(rawTime))
		}
	}
}

func filterValue(v interface {}) interface{} {
	switch v.(type) {
		case time.Time :
		if t, ok := v.(time.Time); ok {
			if agi.IsValidTime(t) {
				return t.Unix()
			} else {
				return 0
			}
		}
	}
	return v
}

//func isDiff2(t reflect.Type, v reflect.Value, raw []byte) bool {
//    // 先把源改为字符串
//    str := string(raw)
//    switch t.Name() {
//        case "int64", "int" :
//        return agi.StrToFInt64(str) != v.Int()
//        case "float64" :
//        return agi.StrToFloat(str) != v.Float()
//        case "Time" :
//        rawTime := agi.IntToTime(agi.StrToFInt64(str))
//        compare := (v.Interface()).(time.Time)
//        return !compare.Equal(rawTime)
//        case "string" :
//        return !strings.EqualFold(string(raw), v.String())
//    }
//    return true
//}

//func Mapping(obj Model, ope Ope) {
//    rt := reflect.TypeOf(obj)
//    el := rt.Elem()
//    var pk string
//    var pkMode = PkNone
//    for i := 0; i < el.NumField(); i++ {
//        f := el.Field(i)
//        tag := f.Tag
//        col := tag.Get(TagColumn)
//        if len(col) <= 0 {
//            continue
//        }
//        fpk := tag.Get(TagPk)
//        if pk == TagValNothing && fpk != TagValNothing {
//            pk = col
//            pkMode = detectPkMode(fpk)
//        }
//    }
//    fmt.Println(pk, pkMode)
//}

func Export(obj Model) map[string]interface{} {
	valueOf := agi.ValueOf(obj)
	mapping := GetModelMapping(obj)
	export := make(map[string]interface{}, mapping.Size)
	for i := 0; i < mapping.Size; i++ {
		col := mapping.Columns[i]
		field := valueOf.Field(i)
		export[col.Column] = filterSaveValue(field)
	}
	return export
}